package com.hzchendou.blog.demo.gateway.security.manager;

import com.hzchendou.blog.demo.gateway.config.WhiteUrlProperties;
import java.util.ArrayList;
import java.util.List;
import net.minidev.json.JSONArray;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthentication;
import org.springframework.security.web.server.authorization.AuthorizationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import reactor.core.publisher.Mono;

/**
 * @Date: 2022-05-13 15:51
 * @since: 1.0
 */
@Component
public class AuthManagerHandler implements ReactiveAuthorizationManager<AuthorizationContext> {

    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Autowired
    private WhiteUrlProperties whiteUrlProperties;

    @Override
    public Mono<AuthorizationDecision> check(Mono<Authentication> authentication,
            AuthorizationContext object) {
        ServerHttpRequest request = object.getExchange().getRequest();
        String requestUrl = request.getPath().pathWithinApplication().value();
        //这里可以根据requestUrl查询redis，或者数据库得到requestUrl所需的角色，放入roles中
        List<String> roles = new ArrayList<>();
        if (whiteUrlProperties.containPath(requestUrl)) {
            roles.add("anonymous");
        } else if (antPathMatcher.match("/blog-resource/admin/**", requestUrl)) {
            roles.add("admin");
        } else if (antPathMatcher.match("/auth/**", requestUrl) || antPathMatcher.match("/admin/**", requestUrl)) {
            roles.add("admin");
            roles.add("user");
        } else {
            roles.add("user");
        }
        if (roles.contains("anonymous")) {
            return Mono.just(new AuthorizationDecision(true));
        }
        return authentication
                .filter(a -> a.isAuthenticated())
                .flatMapIterable(a -> {
                    List<GrantedAuthority> rs = new ArrayList<>();
                    if (a instanceof BearerTokenAuthentication) {
                        JSONArray autors = (JSONArray)((BearerTokenAuthentication) a).getTokenAttributes().get("authorities");
                        if (autors != null && autors.size() > 0) {
                            for (int i = 0; i < autors.size(); i++) {
                                rs.add(new SimpleGrantedAuthority((String) autors.get(i)));
                            }
                        }
                        return rs;
                    }
                    return a.getAuthorities();
                }).map(g -> g.getAuthority())
                .any(c -> {
                    if (roles.contains(String.valueOf(c))) {
                        return true;
                    }
                    return false;
                })
                .map(hasAuthority -> new AuthorizationDecision(hasAuthority))
                .defaultIfEmpty(new AuthorizationDecision(false));
    }

    @Override
    public Mono<Void> verify(Mono<Authentication> authentication, AuthorizationContext object) {
        return Mono.empty();
    }
}